package com.mazaiting.mybatisplus.encrypt.plugins;

import com.mazaiting.mybatisplus.encrypt.FieldEncrypt;
import com.mazaiting.mybatisplus.encrypt.IEncryptor;
import com.mazaiting.mybatisplus.config.MybatisCryptoConfig;
import com.mazaiting.mybatisplus.encrypt.exception.MybatisCryptoException;
import com.mazaiting.mybatisplus.encrypt.provider.EncryptedFieldsProvider;
import com.mazaiting.mybatisplus.encrypt.provider.EncryptorProvider;
import com.mazaiting.mybatisplus.encrypt.utils.MybatisPlusUtil;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Properties;
import java.util.Set;

/**
 * 解密拦截器
 * @author danghailiang
 */
@Intercepts({
        @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
public class MybatisDecryptionPlugin implements Interceptor {

    /**
     * 日志服务
     */
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    /**
     * 快速失败
     */
    private final boolean failFast;
    /**
     * 默认签名
     */
    private final String defaultKey;
    /**
     * 默认加密处理器
     */
    private final Class<? extends IEncryptor> defaultEncryptor;

    /**
     * 初始化参数
     * @param myBatisCryptoConfig 配置类
     */
    public MybatisDecryptionPlugin(MybatisCryptoConfig myBatisCryptoConfig) {
        this.failFast = myBatisCryptoConfig.isFailFast();
        this.defaultKey = myBatisCryptoConfig.getKey();
        this.defaultEncryptor = myBatisCryptoConfig.getEncryptor();
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 获取被加密的对象实体
        Object result = invocation.proceed();
        if (result == null) {
            return null;
        }
        // 是否是ArrayList实现
        if (result instanceof ArrayList resultList) {
            if (resultList.isEmpty()) {
                return result;
            }
            // 获取第一个元素
            Object firstItem = resultList.get(0);
            // 判断参数是否符合条件
            boolean needToDecrypt = MybatisPlusUtil.decryptionRequired(firstItem);
            if (!needToDecrypt) {
                return result;
            }
            // 返回需要被加密属性的set集合
            Set<Field> encryptedFields = EncryptedFieldsProvider.get(firstItem.getClass());
            if (encryptedFields == null || encryptedFields.isEmpty()) {
                return result;
            }
            // 遍历数组
            for (Object item : resultList) {
                decryptEntity(encryptedFields, item);
            }
        } else {
            // 判断
            if (MybatisPlusUtil.decryptionRequired(result)) {
                decryptEntity(EncryptedFieldsProvider.get(result.getClass()), result);
            }
        }
        return result;
    }

    /**
     *
     * @param encryptedFields 加密的属性
     * @param item 具体值
     * @throws MybatisCryptoException 捕获异常
     */
    private void decryptEntity(Set<Field> encryptedFields, Object item) throws MybatisCryptoException {
        if (encryptedFields == null || encryptedFields.isEmpty()) {
            return;
        }
        // 遍历属性
        for (Field field : encryptedFields) {
            // 获取改注解的内容
            FieldEncrypt encryptedField = field.getAnnotation(FieldEncrypt.class);
            if (encryptedField != null) {
                try {
                    // 获取签名
                    String key = MybatisPlusUtil.getKey(encryptedField, defaultKey);
                    // 获取要使用的加密处理器，
                    IEncryptor iEncryptor = EncryptorProvider.get(encryptedField, defaultEncryptor);
                    // 暴力反射
                    field.setAccessible(true);
                    // 获取等待被加密的值
                    Object originalVal = field.get(item);
                    if (originalVal != null) {
                        // 解密
                        String decryptedVal = iEncryptor.decrypt(originalVal.toString(), key);
                        // 从新赋值
                        field.set(item, decryptedVal);
                    }
                } catch (Exception e) {
                    if (failFast) {
                        throw new MybatisCryptoException(e);
                    } else {
                        logger.warn("decrypt filed error.", e);
                    }
                }
            }
        }
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }
}
